package king.dominic.jlibrary.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;

import java.util.Arrays;

import king.dominic.jlibrary.R;

/**
 * Created by king on 2017/3/15.
 * ViewUtil
 */

public class ViewUtil {
    private static final String TAG = "ViewUtil";

    private ViewUtil() {

    }

    public static Animation animationFadeIn(Context context) {
        return AnimationUtils.loadAnimation(context, android.R.anim.fade_in);
    }

    public static Animation animationFadeOut(Context context) {
        return AnimationUtils.loadAnimation(context, android.R.anim.fade_out);
    }

    public static Animation animationSlideInLeft(Context context) {
        return AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
    }

    public static Animation animationSlideOutRight(Context context) {
        return AnimationUtils.loadAnimation(context, android.R.anim.slide_out_right);
    }
    public static Animation animationSlideInRight(Context context) {
        return AnimationUtils.loadAnimation(context, R.anim.slide_in_right);
    }
    public static void dispatchDraw(Canvas canvas, Holder holder) {
        holder.isInvalidate = false;
        if (holder.bounds == null) return;
//        if (holder.actionDown && !holder.targetView.isPressed())
//            holder.actionUp = true;
        boolean needInvalidate = false;
        canvas.save();
        // 为了不让绘制的圆环超出所要绘制的范围
        canvas.clipRect(holder.bounds);
        if (holder.actionDown) {
            float time;
            if (!holder.actionUp) {
                time = sInterpolator.getInterpolation(holder.timeBoundsView());
                time = time > 1 ? 1 : time;
            } else {
                float t = holder.timeBoundsViewCancel();
                t = t > 0 ? t : 0;
                time = sInterpolator.getInterpolation(t);
            }
            holder.transPaint.setAlpha((int) (255 * holder.rectAlpha * time));
            canvas.drawRect(holder.bounds, holder.transPaint);
            needInvalidate = time > 0 && time < 1;
        }

        if (holder.actionUp)
            if (holder.drawRadius < holder.rawRadius) {
                final float time = sInterpolatorCircle.getInterpolation(holder.timeBoundsCircle());
                if (time <= 1f) {
                    holder.drawRadius = holder.rawRadius * time;
                    holder.halfTransPaint.setAlpha((int) (255 * holder.alpha * (1 - (time < 0.8f ? 0.8f : time))));
                    canvas.drawCircle(holder.downPosition[0], holder.downPosition[1], holder.drawRadius, holder.halfTransPaint);
                    needInvalidate = true;
                }
            } else {
                holder.clear();
            }
        canvas.restore();
        if (!holder.isInvalidate && needInvalidate) {
            holder.isInvalidate = true;
            holder.view.postInvalidateDelayed(Holder.INVALID_DURATION);
        }
    }

    private static final Interpolator sInterpolator = new Interpolator() {
        @Override
        public float getInterpolation(float t) {
            t -= 1.0f;
            return t * t * t * t * t + 1.0f;
        }
    };

    private static final Interpolator sInterpolatorCircle = new Interpolator() {
        @Override
        public float getInterpolation(float t) {
            t -= 1.0f;
            return (t * t * t * t * t + 1.0f) * 0.5f + 0.5f;
        }
    };

    public static boolean dispatchTouchEvent(MotionEvent event, Holder holder) {
        holder.event = event;
        boolean needInvalidate = false;
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                if (holder.targetViewBounds == null || holder.targetViewBounds.contains(event.getRawX(), event.getRawY()))
                    break;
            case MotionEvent.ACTION_CANCEL:
                holder.actionCancel = true;
            case MotionEvent.ACTION_UP:
                holder.actionUp = true;
                needInvalidate = true;
                break;
            case MotionEvent.ACTION_DOWN:
                holder.clear();
                holder.actionDown = true;

                final float circleCenterX = event.getRawX();
                final float circleCenterY = event.getRawY();

                View targetView = findViewByXY(holder.view, circleCenterX, circleCenterY);

                if (targetView != null) {

                    RectF targetViewBounds = getViewRectF(targetView);
                    holder.targetView = targetView;
                    holder.targetViewBounds = targetViewBounds;

                    RectF viewBounds = getViewRectF(holder.view);
                    holder.bounds = new RectF(
                            targetViewBounds.left - viewBounds.left,
                            targetViewBounds.top - viewBounds.top,
                            targetViewBounds.right - viewBounds.left,
                            targetViewBounds.bottom - viewBounds.top);


                    // 计算出最大的值则为半径
                    float left = circleCenterX - targetViewBounds.left;
                    float right = targetViewBounds.right - circleCenterX;
                    float top = circleCenterY - targetViewBounds.top;
                    float bottom = targetViewBounds.bottom - circleCenterY;
                    holder.rawRadius = Math.max(bottom, Math.max(Math.max(left, right), top));
                    if (WindowUtil.width > 0) {
                        holder.rawRadius = Math.min(holder.rawRadius, WindowUtil.width * 0.309f);
                    }
//                    holder.rawRadius = Math.max(holder.bounds.width() / 2, holder.bounds.height() / 2);

                    int[] location = new int[2];
                    holder.downPosition = new float[2];
                    holder.view.getLocationOnScreen(location);
                    holder.downPosition[0] = circleCenterX - location[0];
                    holder.downPosition[1] = circleCenterY - location[1];

                    needInvalidate = true;
                }
                break;
        }

        if (!holder.isInvalidate && needInvalidate) {
            holder.isInvalidate = true;
            holder.view.postInvalidateDelayed(Holder.INVALID_DURATION);
        }
        return holder.dispatchTouchEvent.dispatchTouchEvent(event);
    }

    /**
     * 根据坐标获取相对应的子控件<br>
     * 在重写ViewGroup使用
     *
     * @param x 坐标
     * @param y 坐标
     * @return 目标View
     */
    private static View findViewByXY(View view, float x, float y) {
        View targetView = getTouchTarget(view, x, y);
        if (targetView == null && view instanceof ViewGroup) {
            // 父容器,遍历子控件
            ViewGroup v = (ViewGroup) view;
            for (int i = 0; i < v.getChildCount(); i++) {
                targetView = findViewByXY(v.getChildAt(i), x, y);
                if (targetView != null) {
                    break;
                }
            }
        }
        return targetView;

    }

    private static View getTouchTarget(View view, float x, float y) {
        View targetView = null;
        // 判断view是否可以聚焦
        if (isTouchPointInView(view, x, y))
            targetView = view;
        return targetView;
    }

    private static boolean isTouchPointInView(View view, float x, float y) {
        RectF bounds = getViewRectF(view);
        return (view.isClickable() && bounds.contains(x, y));
    }

    private static RectF getViewRectF(View view) {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int childLeft = location[0];
        int childTop = location[1];
        int childRight = childLeft + view.getMeasuredWidth();
        int childBottom = childTop + view.getMeasuredHeight();
        return new RectF(childLeft, childTop, childRight, childBottom);
    }

    public interface OnDispatchTouchEvent {
        boolean dispatchTouchEvent(MotionEvent event);
    }

    public static class Holder {
        private static final float DRAWING_RADIUS_DEGREES = 0.02f;
        private static final long INVALID_DURATION = 10;
        private static final float DRAWING_RECT_DEGREES = 0.01f;
        private static final float DRAWING_RECT_DEGREES_CANCEL = 0.02f;
        private final View view;
        private final OnDispatchTouchEvent dispatchTouchEvent;
        private RectF bounds;
        private float rawRadius;
        private float drawRadius = 0;
        private float[] downPosition;
        private Paint halfTransPaint;
        private Paint transPaint;
        private float timeBoundsCircle = 0;
        private float alpha;
        private int color;
        private boolean actionDown;
        private boolean actionUp;
        private float timeBoundsView = 0;
        private long previousNanoTimeBoundsCircle = 0;
        private long previousNanoTimeBoundsView = 0;
        private volatile boolean isInvalidate = false;
        private float rectAlpha;
        private long previousNanoTimeBoundsViewCancel = 0;
        private View targetView;
        private RectF targetViewBounds;
        private boolean actionCancel = false;
        private MotionEvent event;

        public Holder(View view, OnDispatchTouchEvent dispatchTouchEvent) {
            this.view = view;
            this.dispatchTouchEvent = dispatchTouchEvent;
            transPaint = new Paint();
            halfTransPaint = new Paint();
            setColor(Color.parseColor("#999999"), 1f);
        }

        private void clear() {
            actionCancel = false;
            actionDown = false;
            actionUp = false;
            targetView = null;
            targetViewBounds = null;
            previousNanoTimeBoundsViewCancel = 0;
            previousNanoTimeBoundsCircle = 0;
            previousNanoTimeBoundsView = 0;
            timeBoundsCircle = 0;
            timeBoundsView = 0;
            drawRadius = 0;
            bounds = null;
            setColor(color, alpha);
        }

        private void setColor(int color, float alpha) {
            this.alpha = alpha;
            this.rectAlpha = 0.1f * alpha;
            this.color = color;
            transPaint.setColor(color);
            transPaint.setAlpha((int) (rectAlpha * 255));
            halfTransPaint.setColor(color);
            halfTransPaint.setAlpha((int) (alpha * 255));
        }

        @Override
        public String toString() {
            return "Holder{" +
                    "bounds=" + bounds +
                    ", rawRadius=" + rawRadius +
                    ", drawRadius=" + drawRadius +
                    ", downPosition=" + Arrays.toString(downPosition) +
                    ", halfTransPaint=" + halfTransPaint +
                    ", transPaint=" + transPaint +
                    '}';
        }

        private float timeBoundsCircle() {
            long currentNanoTime = System.nanoTime();
            if (previousNanoTimeBoundsCircle == 0)
                previousNanoTimeBoundsCircle = currentNanoTime - INVALID_DURATION * 1000000;
            timeBoundsCircle += DRAWING_RADIUS_DEGREES * (currentNanoTime - previousNanoTimeBoundsCircle) / 1000000 / INVALID_DURATION;
            previousNanoTimeBoundsCircle = currentNanoTime;
            return timeBoundsCircle;
        }

        private float timeBoundsView() {
            long currentNanoTime = System.nanoTime();
            if (previousNanoTimeBoundsView == 0)
                previousNanoTimeBoundsView = currentNanoTime - INVALID_DURATION * 1000000;
            timeBoundsView += DRAWING_RECT_DEGREES * (currentNanoTime - previousNanoTimeBoundsView) / 1000000 / INVALID_DURATION;
            previousNanoTimeBoundsView = currentNanoTime;
            return timeBoundsView;
        }

        private float timeBoundsViewCancel() {
            if (timeBoundsView > 1) timeBoundsView = 1;
            long currentNanoTime = System.nanoTime();
            if (previousNanoTimeBoundsViewCancel == 0)
                previousNanoTimeBoundsViewCancel = currentNanoTime - INVALID_DURATION * 1000000;
            timeBoundsView -= DRAWING_RECT_DEGREES_CANCEL * (currentNanoTime - previousNanoTimeBoundsViewCancel) / 1000000 / INVALID_DURATION;
            previousNanoTimeBoundsViewCancel = currentNanoTime;
            return timeBoundsView;
        }
    }
}
